﻿using Xejen.Communication.Configuration;
using Xejen.Communication.Socket.Abstract;
using Xejen.Communication.Socket.Configuration;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Xejen.Communication.Socket
{
    /// <summary>
    /// 使用<see langword="TcpClient"/>及<see langword="TcpListener"/>的技术向远程服务器发起请求
    /// </summary>
    /// <creator>marc</creator>
    internal class TcpInvoker : ISocketInvoker
    {
        private readonly ConnectionConfig _config;

        private bool _disposed;
        private TcpClient _tcpClient;

        /// <inheritdoc cref="TcpInvoker"/>
        protected TcpInvoker() { }

        /// <inheritdoc cref="TcpInvoker"/>
        public TcpInvoker(ConnectionConfig config)
        {
            Check.NotNull(config, nameof(config));

            _config = config;

            if (config.ConnectMode == ConnectMode.Long)
            {
                _tcpClient = CreateSocket();
            }
        }

        /// <summary>
        /// 创建一个新的Tcp客户端
        /// </summary>
        /// <returns></returns>
        private TcpClient CreateSocket()
        {
            var tcpClient = new TcpClient();
            tcpClient.SendBufferSize = _config.SendBufferSize;
            tcpClient.ReceiveBufferSize = _config.ReceiveBufferSize;
            tcpClient.SendTimeout = _config.SendTimeout;
            tcpClient.ReceiveTimeout = _config.ReceiveTimeout;

            return tcpClient;
        }

        /// <inheritdoc/>
        public async Task<SocketResponse> SendAsync(byte[] input, int timeout = -1, CancellationToken cancellationToken = default)
        {
            return await InnertSendAsync(input, timeout, cancellationToken);
        }

        private async Task<SocketResponse> InnertSendAsync(byte[] input, int timeout = -1, CancellationToken cancellationToken = default)
        {
            _tcpClient = this._tcpClient ?? CreateSocket();

            var connectTask = _tcpClient.ConnectAsync(_config.ServerAddress, _config.ServerPort);

            var completedTask = await Task.WhenAny(connectTask, Task.Delay(timeout, cancellationToken));

            if (completedTask != connectTask)
            {
                throw new TimeoutException($"Socket连接超时，超时时间限定：{timeout} 毫秒");
            }

            await connectTask;

            using (NetworkStream networkStream = _tcpClient.GetStream())
            {
                byte[] dataBytes = input;// Config.Encoding.GetBytes(JsonSerializer.Serialize(input, JsonSerializerOptions));

                var sendTask = networkStream.WriteAsync(dataBytes, 0, dataBytes.Length, cancellationToken);

                var receiveTask = ReceiveAsync(networkStream, cancellationToken);

                await Task.WhenAll(sendTask, receiveTask);

                SocketResponse result = SocketResponse.Success(receiveTask.Result, encoding: _config.Encoding);

                if (_config.ConnectMode == ConnectMode.Short)
                {
                    Close();
                }

                return result;
            }
        }

        private async Task<byte[]> ReceiveAsync(NetworkStream networkStream, CancellationToken cancellationToken = default)
        {
            List<byte> dataBuffer = new List<byte>();

            byte[] tempBuffer = new byte[4096];

            while (true)
            {
                var receiveTask = networkStream.ReadAsync(tempBuffer, 0, tempBuffer.Length, cancellationToken);

                int bytesRead = await receiveTask;

                if (bytesRead == 0)
                {
                    // 连接已关闭，可以根据需要处理
                    break;
                }

                dataBuffer.AddRange(tempBuffer.Take(bytesRead));

                if (bytesRead < tempBuffer.Length)
                {
                    break;
                }
            }

            return dataBuffer.ToArray();
        }

        /// <inheritdoc/>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Close()
        {
            _tcpClient?.Close();
            _tcpClient?.Dispose();
            _tcpClient = null;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                Close();
            }

            _disposed = true;
        }
    }
}
